package com.zjmzxfzhl.modules.sys.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zjmzxfzhl.common.core.base.BaseServiceImpl;
import com.zjmzxfzhl.common.core.constant.ScheduleConstants;
import com.zjmzxfzhl.common.core.exception.SysException;
import com.zjmzxfzhl.modules.sys.config.job.util.CronUtil;
import com.zjmzxfzhl.modules.sys.config.job.util.ScheduleUtil;
import com.zjmzxfzhl.modules.sys.entity.SysJob;
import com.zjmzxfzhl.modules.sys.mapper.SysJobMapper;
import com.zjmzxfzhl.modules.sys.service.SysJobService;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * 定时任务Service
 *
 * @author 庄金明
 */
@Service
public class SysJobServiceImpl extends BaseServiceImpl<SysJobMapper, SysJob> implements SysJobService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void init() throws SchedulerException {
        scheduler.clear();
        List<SysJob> jobList = baseMapper.list(null, new SysJob());
        for (SysJob job : jobList) {
            ScheduleUtil.createScheduleJob(scheduler, job);
        }
    }

    @Override
    public IPage<SysJob> list(IPage<SysJob> page, SysJob sysJob) {
        return page.setRecords(baseMapper.list(page, sysJob));
    }

    /**
     * 新增任务
     *
     * @param job 调度信息 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveJob(SysJob job) throws SchedulerException {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        boolean result = save(job);
        if (result) {
            ScheduleUtil.createScheduleJob(scheduler, job);
        }
        return result;
    }

    /**
     * 更新任务的时间表达式
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateJob(SysJob job) throws SchedulerException {
        SysJob jobDb = this.getById(job.getJobId());
        boolean result = this.updateById(job);
        if (result) {
            updateSchedulerJob(job, jobDb.getJobGroup());
        }
        return result;
    }

    /**
     * 更新任务
     *
     * @param job      任务对象
     * @param jobGroup 任务组名
     */
    private void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException {
        String jobId = job.getJobId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtil.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtil.createScheduleJob(scheduler, job);
    }

    /**
     * 暂停任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pause(SysJob job) throws SchedulerException {
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        if (updateById(job)) {
            scheduler.pauseJob(ScheduleUtil.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 恢复任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resume(SysJob job) throws SchedulerException {
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        if (updateById(job)) {
            scheduler.resumeJob(ScheduleUtil.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 删除任务后，所对应的trigger也将被删除
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(SysJob job) throws SchedulerException {
        String jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        if (removeById(jobId)) {
            scheduler.deleteJob(ScheduleUtil.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 批量删除调度信息
     *
     * @param jobIds 需要删除的任务ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String ids) throws SchedulerException {
        if (ids == null || ids.trim().length() == 0) {
            throw new SysException("ids can't be empty");
        }
        String[] idsArr = ids.split(",");
        for (String jobId : idsArr) {
            SysJob job = this.getById(jobId);
            delete(job);
        }
    }

    /**
     * 任务调度状态修改
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatus(String jobId) throws SchedulerException {
        SysJob sysJob = this.getById(jobId);
        sysJob.setUpdateBy(null);
        sysJob.setUpdateDate(null);
        sysJob.setUpdateTime(null);
        String status = sysJob.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            pause(sysJob);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            resume(sysJob);
        }
    }

    /**
     * 立即运行任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void run(String jobId) throws SchedulerException {
        SysJob sysJob = this.getById(jobId);
        String jobGroup = sysJob.getJobGroup();
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, sysJob);
        scheduler.triggerJob(ScheduleUtil.getJobKey(jobId, jobGroup), dataMap);
    }

    /**
     * 校验cron表达式是否有效
     *
     * @param cronExpression 表达式
     * @return 结果
     */
    @Override
    public boolean checkCronExpressionIsValid(String cronExpression) {
        return CronUtil.isValid(cronExpression);
    }
}
